package com.whnk.pm.service.impl;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.stream.Collectors;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import com.whnk.common.support.Convert;
import com.whnk.pm.domain.IncomePro;
import com.whnk.pm.dto.IncImpErrorDto;
import com.whnk.pm.mapper.IncomeProMapper;
import com.whnk.pm.service.IIncomeProService;
import com.whnk.pm.service.impl.EmpInfoImportServcieImpl.ErrorMsgEnum;
import com.whnk.pm.util.ValidateException;
import com.whnk.pm.util.ValidateUtil;

import cn.hutool.core.io.FileUtil;
import cn.hutool.poi.excel.ExcelReader;
import cn.hutool.poi.excel.ExcelUtil;
import lombok.extern.slf4j.Slf4j;

/**
 * 月度收入报送（按项目） 服务层实现
 * 
 * @author whwhnk
 * @date 2022-09-06
 */
@Service
@Slf4j
public class IncomeProServiceImpl implements IIncomeProService 
{
	@Autowired
	private IncomeProMapper incomeProMapper;
	
	private List<IncImpErrorDto> errorList = null;

	/**
     * 查询月度收入报送（按项目）
     * 
     * @param id 月度收入报送（按项目）信息ID
     * @return 月度收入报送（按项目）信息
     */
	@Override
	public IncomePro selectIncomeProById(Integer id) {
		return incomeProMapper.selectIncomeProById(id);
	}
	
	/**
     * 查询月度收入报送（按项目）列表
     * 
     * @param incomePro 月度收入报送（按项目）信息
     * @return 月度收入报送（按项目）集合
     */
	@Override
	public List<IncomePro> selectIncomeProList(IncomePro incomePro)
	{
	    return incomeProMapper.selectIncomeProList(incomePro);
	}
	
    /**
     * 新增月度收入报送（按项目）
     * 
     * @param incomePro 月度收入报送（按项目）信息
     * @return 结果
     */
	@Override
	public int insertIncomePro(IncomePro incomePro)
	{
	    return incomeProMapper.insertIncomePro(incomePro);
	}
	
	/**
     * 修改月度收入报送（按项目）
     * 
     * @param incomePro 月度收入报送（按项目）信息
     * @return 结果
     */
	@Override
	public int updateIncomePro(IncomePro incomePro)
	{
	    return incomeProMapper.updateIncomePro(incomePro);
	}

	/**
     * 删除月度收入报送（按项目）对象
     * 
     * @param ids 需要删除的数据ID
     * @return 结果
     */
	@Override
	public int deleteIncomeProByIds(String ids)
	{
		return incomeProMapper.deleteIncomeProByIds(Convert.toStrArray(ids));
	}
	
	/**
	 * 根据报送年月和项目名称计算报送统计表
	 * 
	 * @param wlYm  报送年月
	 * @param proName 项目名称
	 * @return
	 */
	@Override
	public void countByWlYm(String wlYm, String proName) {
		
		// 设置参数
		IncomePro incomePro = new IncomePro();
		incomePro.setWlYm(wlYm); // 报送年月（格式：YYYYMM）
		incomePro.setProName(proName); // 项目名称（模糊查询）
		
		// 根据条件，查询收入信息表
		List<IncomePro> incList  = incomeProMapper.selectIncomeProList(incomePro);
		
		// 将收入数据列表按照项目分组
		Map<String, List<IncomePro>> proMap = incList.stream().collect(Collectors.groupingBy(IncomePro::getProSn,Collectors.toList()));
		
		// 按照项目维度，循环计算每个项目的收入数据
		Iterator<Map.Entry<String, List<IncomePro>>> it = proMap.entrySet().iterator();
		while (it.hasNext()) {
			
			Map.Entry<String, List<IncomePro>> entry = it.next();
			List<IncomePro> incsOfPro = entry.getValue();
			
			// 暂存各个级别的当月产生人月数
			Map<String, Double> monsByLvMap = new HashMap<String,Double>();
			
			log.info("------------------------------------项目编号："+entry.getKey()+"，开始------------------------------------");
			
			/**
			 * 从一个项目的最高级别开始计算，如果该项目最高级别的累计到当前的人月数，已经超过该级别的订单人月数，则将超过的部分降一级计入收入；
			 * 如果从上一级降级的收入加上本级累计到当前月的收入后，超过本级的订单人月数，则继续降一级计入收入；依次类推。
			 * 如果降到初级后，降级的收入加上累计到当前的初级人月数，超过了该级别的订单人月数，则超出部分收入不能计入。
			 */
			
			String logTitle = "";
			
			// 超出部分的人月数
			double beyondPmons=0;
			
			for (int i = 0; i < incsOfPro.size(); i++) {
				IncomePro inc  = incsOfPro.get(i);

				Double ctrPmons = inc.getCtrPmons(); // 订单人月数
				
				//  订单人月数为0，则
				if(ctrPmons==0) {
					
					// 设置本月可报送人月数
					inc.setWlCurrPmons( 0.0 );
					
					// 设置本月可报送收入金额=单价 * 本月报送人月数
					inc.setWlCurrTotal( 0.0 );
					
					// 设置累计到上月金额=单价 * 累计到上月人月数
					inc.setWlLastTotal( 0.0 );
					
					// 设置累计到本月金额=单价 * 累计到本月人月数
					inc.setWlTotal( 0.0 );
					
				}else {
						
					// 累计
					Double wlLastPmons = inc.getWlLastPmons(); // 累计到上月人月数
					Double wlPmons = inc.getWlPmons(); // 累计到本月人月数
					
					// 剩余人月数=订单人月数-累计到本月人月数
					double remainPmos = ctrPmons.doubleValue()-wlPmons.doubleValue();
					
					// 本月产生人月数=累计到本月人月数-累计到上月人月数
					double usedCurrPmons = wlPmons.doubleValue()-wlLastPmons.doubleValue();
					
					// 人员级别
					String lv = inc.getCtrLevel();
					
					// 将级别对应的本月产生人月数，存入Map
					monsByLvMap.put(lv, usedCurrPmons);
					
					// 累加超出人月数
					beyondPmons = remainPmos+beyondPmons;
					
					if(  beyondPmons >=0 ) { //（剩余人月数累加：剩余人月数+上个级别降级的人月数）<= 订单人月数
						
						// 累加高级别的人月数
						Iterator<Map.Entry<String,Double>> lvMons = monsByLvMap.entrySet().iterator();
						while (lvMons.hasNext()) {
							Entry<String,Double> lvMon = lvMons.next();
							
							// 如果级别比当前级别高，则累加；否则，不做处理。
							if(Integer.valueOf(lv)<Integer.valueOf(lvMon.getKey())) 
								usedCurrPmons += lvMon.getValue();
						}
						
						// 超出人月数重置为0
						beyondPmons = 0;
						
						logTitle = "【（累计到本月人月数+上个级别降级的人月数）<=订单人月数】=== ";
						
					}else { //（剩余人月数累加：剩余人月数+上个级别降级的人月数）> 订单人月数
						
						// 累加高级别的人月数
						Iterator<Map.Entry<String,Double>> lvMons = monsByLvMap.entrySet().iterator();
						while (lvMons.hasNext()) {
							Entry<String,Double> lvMon = lvMons.next();
							
							// 如果级别比当前级别高，则累加；否则，不做处理。
							if(Integer.valueOf(lv)<Integer.valueOf(lvMon.getKey())) 
								usedCurrPmons += lvMon.getValue();
						}
						
						usedCurrPmons = ( usedCurrPmons+beyondPmons<0 ) ? 0 :  (usedCurrPmons+beyondPmons) ;
						
						logTitle = "【（累计到本月人月数+上个级别降级的人月数）>订单人月数】=== ";
					}
					
					// 设置本月可报送人月数
					inc.setWlCurrPmons( ValidateUtil.doubleForRound(usedCurrPmons,2) );
					
					Double ctrUnp = inc.getCtrUnp(); // 订单人员单价
					
					// 设置本月可报送收入金额=单价 * 本月报送人月数
					inc.setWlCurrTotal( ValidateUtil.doubleForRound(ctrUnp*usedCurrPmons,2) );
					
					// 设置累计到上月金额=单价 * 累计到上月人月数
					inc.setWlLastTotal( ValidateUtil.doubleForRound(ctrUnp*wlLastPmons,2) );
					
					// 设置累计到本月金额=单价 * 累计到本月人月数
					inc.setWlTotal( ValidateUtil.doubleForRound(ctrUnp*wlPmons,2) );
					
					}		
				
				// 更新数据库记录
				incomeProMapper.updateIncomePro(inc);
				
				log.info(logTitle +"订单级别："+inc.getCtrLevel()+"；订单人月数："+ inc.getCtrPmons() + "；累计到上月人月数："+ inc.getWlLastPmons() +"；累计到本月人月数："+ inc.getWlPmons() +"；累计超出人月数："+  ValidateUtil.doubleForRound(beyondPmons,2)+"；本月可报送人月数："+ inc.getWlCurrPmons());
			
			}
			log.info("------------------------------------项目编号："+entry.getKey()+"，结束------------------------------------");
		}
	
	}

	/**
	 * 导入月度收入数据EXCEL
	 * 
	 * @param fileName
	 * @param msg
	 */
	@Transactional(rollbackFor = { Exception.class }) // 手动回滚
	@Override
	public void impExcel(String fileName, List<IncImpErrorDto> msg) {
		errorList = new ArrayList<IncImpErrorDto>();
		try {
			// 基本信息导入
			addBatchIncInfo(fileName);
			
			if (errorList.size() > 0) {
				errorList.forEach(item -> System.out.println(item.toString()));
				throw new ValidateException("校验不通过.......");
			}
		} catch (ValidateException e) {
			throw new ValidateException("校验不通过.......");
		} catch (Exception e) {
			log.info("导入异常");
			log.error("error---->", e);
			throw new RuntimeException("导入异常");
		} finally {
			msg.addAll(errorList);
		}
	}
	
		/**
		 * 将EXCEL的数据写入数据表
		 * 
		 * @param fileName
		 * @param sheetName
		 * @return
		 */
		private void addBatchIncInfo(String fileName) {
			
			// 读取EXCEL文件的数据
			List<List<Object>> readAll = readSheet(fileName);
			
			// 批量插入的数据列表
			List<IncomePro> incInfoList = new ArrayList<IncomePro>();
			
			// 批量修改的数据列表
			List<IncomePro> updateIncInfoList = new ArrayList<IncomePro>();
			
			Boolean errorFlag = false;// 错误标志
			
			for (List<Object> row : readAll) {
				IncomePro incInfo = new IncomePro();
				int col = 0;
				
				// 设置字段值
				incInfo.setProSn(ValidateUtil.objToString(row.get(col++))); // 项目编号
				incInfo.setProName(ValidateUtil.objToString(row.get(col++))); // 项目名称
				incInfo.setCtrSn(ValidateUtil.objToString(row.get(col++))); // 订单编号
				incInfo.setCtrLevel(ValidateUtil.objToString(row.get(col++))); // 订单人员级别（0初级；1中级；2高级；3专家）
				incInfo.setCtrPmons(ValidateUtil.objToDouble(row.get(col++))); // 订单人月数
				incInfo.setCtrUnp(ValidateUtil.objToDouble(row.get(col++))); // 订单人员单价
				incInfo.setCtrTotal(ValidateUtil.objToDouble(row.get(col++))); // 订单金额
				incInfo.setWlLastPmons(ValidateUtil.objToDouble(row.get(col++))); // 累计到上月人月数
				incInfo.setWlPmons(ValidateUtil.objToDouble(row.get(col++))); // 累计到本月人月数
				incInfo.setWlLastTotal(ValidateUtil.objToDouble(row.get(col++))); // 累计到上月金额
				incInfo.setWlTotal(ValidateUtil.objToDouble(row.get(col++))); // 累计到本月金额
				incInfo.setWlCurrPmons(ValidateUtil.objToDouble(row.get(col++))); // 本月上报人月数
				incInfo.setWlCurrTotal(ValidateUtil.objToDouble(row.get(col++))); // 本月上报金额
				incInfo.setWlYm(ValidateUtil.objToString(row.get(col++))); // 年月（YYYYMM）

				if (validMonIncInfo(incInfo)) {
					errorFlag = true;
				}

				// 设置查询参数
				IncomePro incInfoParm = new IncomePro();
				incInfoParm.setWlYm(incInfo.getWlYm());
				incInfoParm.setProSn(incInfo.getProSn());
				incInfoParm.setCtrLevel(incInfo.getCtrLevel());
				
				// 根据年月、项目编号、订单人员级别三个条件查询数据库记录
				List<IncomePro> checkList = incomeProMapper.selectIncomeProList(incInfoParm);
				
				log.info("addBatchIncInfo()==="+CollectionUtils.isEmpty(checkList));
				
				if(CollectionUtils.isEmpty(checkList)) // 没有查到记录，则新增
					incInfoList.add(incInfo);
				else  // 查到记录，则修改
					updateIncInfoList.add(incInfo);
			}
			if (!errorFlag) {
				incomeProMapper.insertBatchIncInfo(incInfoList); // 批量新增
				incomeProMapper.updateBatchIncInfo(updateIncInfoList);  // 批量修改
			}
		}
	
	/**
	 * 校验导入的月度收入数据
	 * 
	 * @param incInfo
	 * @param sheetName
	 * @return
	 */
	private Boolean validMonIncInfo(IncomePro incInfo) {
		Boolean errorFlag = false;
		
		// 检查必须输入项
		if (StringUtils.isAnyEmpty(new String[] { incInfo.getProSn(), incInfo.getProName(), incInfo.getCtrLevel(), 
				String.valueOf(incInfo.getCtrPmons()), String.valueOf(incInfo.getCtrUnp()),String.valueOf(incInfo.getCtrTotal()),String.valueOf(incInfo.getWlYm()) })) {
			errorFlag = true;
			errorList.add(new IncImpErrorDto(incInfo.getProSn(), incInfo.getProName(),
					ErrorMsgEnum.必填项检查失败.getErrorMsg()));
		}

		return errorFlag;
	}

	/**
	 * 读取EXCEL中的指定sheet页的数据
	 * 
	 * @param fileName
	 * @param sheetName
	 * @return
	 */
	private List<List<Object>> readSheet(String fileName) {
		// 读取第一个sheet页
		ExcelReader reader = ExcelUtil.getReader(FileUtil.file(fileName),0);
		// 总行数
		int rowCount = reader.getRowCount();
		// 固定不解析行数
		int fixDelRowCount = 0;
		return reader.read(1, rowCount - 1 - fixDelRowCount);
	}

}
